/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// NonCopyable
#include "utility/noncopyable.h"
// nx::lock_t
#include "thread/lock.h"
// type::Proxy
#include "type/typetools.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

template <typename T>
class Singleton : NonCopyable
{
protected:
    static lock_t lc_;

public:
    static T& instance(void)
    {
#ifndef NX_SINGLE_THREAD

        class Locker_
        {
        protected:
            lock_t& lc_;
        public:
            Locker_(lock_t& lc) : lc_(lc) { lc_.lock(); }
            ~Locker_()                    { lc_.unlock(); }
        };

        static volatile T* inst_p = NX_NULL;
        /*
            Double-checked locking can be implemented in Visual C++ 2005 and above
            if the pointer to the resource is declared with the C++ keyword volatile.
            (See http://en.wikipedia.org/wiki/Double-checked_locking)
        */
#   if defined(NX_CC_GNUC)
        nx_atomic_barrier();
#   endif
        if (!inst_p)
        {
            Locker_ locker(lc_); (void)locker;
            if (!inst_p)
            {
                static T inst_r;
#           if defined(NX_CC_GNUC)
                nx_atomic_barrier();
#           endif
                inst_p = &inst_r;
            }
        }
        return (*(const_cast<T*>(inst_p)));

#else /*NX_SINGLE_THREAD*/

        static T inst_r;
        return inst_r;

#endif/*NX_SINGLE_THREAD*/
    }
};

template <typename T>
lock_t Singleton<T>::lc_;

/*
    singleton function
*/

template <typename T>
T& singleton(void) { return Singleton<T>::instance(); }

template <typename P, typename T>
T& singleton(void) { return singleton<type::Proxy<T, P> >(); }

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
